#pragma once

#include "Misc/EngineEditorMacros.h"
#include "Framework/ApplicationSubsystem.h"
#include "Framework/SubsystemManager.h"
#include "Framework/EditorModelSubsystem.h"
#include "Framework/EditorViewSubsystem.h"
#include "Framework/EditorViewModelSubsystem.h"
#include "Project/ProjectInfo.h"
#include "Editor.m.h"

class REditorModelSubsystem;
class RSubsystemManager;
class REditorTheme;

RSTRUCT()
struct SEditorSetting
{
    ROBJECT

    //history project infos
    //first one is the newest
    RFIELD()
    TVector<SProjectPathInfo> HistoryProjectInfos;

};


//root object of whole editor
RCLASS(ExtraHeader = "Framework/EditorModelSubsystem.h")
class REditor : public RApplicationSubsystem
{
    ROBJECT

public:
    void Initialize() override;
    void Deinitialize() override;
    static REditor* Get();

    struct FEditorOpenResult
    {
        //the editor is empty require to open a project
        //or some pages.
        //when user first time open editor, bEmpty will be true
        bool bEmpty=true;
    };

    //open editor
    FEditorOpenResult Open();

    void Exit();

    //get the root object of model
    //all model objects should be child of this object
    //this object is used to save editor state
    RObject* GetRootOfModel() { return ModelSubsystemManager.Get(); }

    template<typename T>
    void MakeSureInitialized()
    {
        if(T::StaticClass()->CanObjectConvertTo(REditorModelSubsystem::StaticClass()))
        {
            ModelSubsystemManager->MakeSureInitialized<T>();
        }
        else if(T::StaticClass()->CanObjectConvertTo(REditorViewSubsystem::StaticClass()))
        {
            ViewSubsystemManager->MakeSureInitialized<T>();
        }
        else if(T::StaticClass()->CanObjectConvertTo(REditorViewModelSubsystem::StaticClass()))
        {
            ViewModelSubsystemManager->MakeSureInitialized<T>();
        }
        else
        {
            assert(false && "invalid subsystem type");
        }
    }

    template<typename T>
    TSharedObjectPtr<T> GetSubsystem()
    {
        if(T::StaticClass()->CanObjectConvertTo(REditorModelSubsystem::StaticClass()))
        {
            return ModelSubsystemManager->GetSubsystem<T>();
        }
        else if(T::StaticClass()->CanObjectConvertTo(REditorViewSubsystem::StaticClass()))
        {
            return ViewSubsystemManager->GetSubsystem<T>();
        }
        else if(T::StaticClass()->CanObjectConvertTo(REditorViewModelSubsystem::StaticClass()))
        {
            return ViewModelSubsystemManager->GetSubsystem<T>();
        }
        else
        {
            return nullptr;
        }
    }

protected:
    //parent object of all model objects
    //this object is used to save editor state
    TSharedObjectPtr<RSubsystemManager>  ModelSubsystemManager;
    TSharedObjectPtr<RSubsystemManager>  ViewModelSubsystemManager;
    TSharedObjectPtr<RSubsystemManager>  ViewSubsystemManager;



public:
    void RegisterMVVM(CClass* Model, CClass* ViewModel, CClass* View)
    {
        ModelToViewModelMap.Add(Model, ViewModel);
        ViewModelToViewMap.Add(ViewModel, View);
    }

    CClass* GetViewModelClass(CClass* Model)
    {
        return ModelToViewModelMap[Model];
    }

    CClass* GetViewClass(CClass* ViewModel)
    {
        return ViewModelToViewMap[ViewModel];
    }

protected:
    TMap<CClass* /*Model*/, CClass* /*ViewModel*/> ModelToViewModelMap;
    TMap<CClass* /*ViewModel*/, CClass* /*View*/> ViewModelToViewMap;



public:
    SEditorSetting CurrentSetting;

    void SaveSetting();

    void AddHistoryProjectInfo(const SProjectPathInfo& InProjectInfo);
protected:

    SPath GetSettingFilePath();

    void InitSetting();

    void SaveEditorState();
public:
    TSharedObjectPtr<REditorTheme> CurrentTheme;
};